﻿using System;
using System.IO.Ports;

namespace Sttplay.Transmission.SerialPortEx
{
    public class SerialPortEx
    {
        private string com;
        private int baudRate;
        private Parity parity;
        private int dataBits;
        private StopBits stopBits;
        public bool RTS { get; set; }
        public bool DTR { get; set; }
        public static string[] SerialPorts { get { return SerialPort.GetPortNames(); } }
        private SerialPort serial;
        public event Action<byte[]> OnReceiveEvent;
        public event Action<string> OnErrorLogEvent;
        public SerialPortEx()
        {
            DTR = RTS = true;
        }

        public bool Open(string com, int baudRate, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One)
        {
            Close();
            this.com = com;
            this.baudRate = baudRate;
            this.parity = parity;
            this.dataBits = dataBits;
            this.stopBits = stopBits;
            try
            {
                serial = new SerialPort(com, baudRate, parity, dataBits, stopBits);
                serial.RtsEnable = RTS;
                serial.DtrEnable = DTR;
                serial.Open();
                serial.DiscardInBuffer();
                serial.DiscardOutBuffer();
            }
            catch (Exception ex)
            {
                ErrorLog(ex.ToString());
                return false;
            }
           
            return true;
        }

        public bool Write(byte[] buffer, int index, int len)
        {
            if (serial == null) return false;
            try
            {
                serial.Write(buffer, index, len);
                return true;
            }
            catch (Exception ex)
            {
                ErrorLog(ex.ToString());
                return false;
            }
        }

        public bool Write(byte[] buffer)
        {
            if (serial == null) return false;
            try
            {
                serial.Write(buffer, 0, buffer.Length);
                return true;
            }
            catch (Exception ex)
            {
                ErrorLog(ex.ToString());
                Close();
                return false;
            }
        }

        public bool ReOpen()
        {
            return Open(com, baudRate, parity, dataBits, stopBits);
        }

        public void Close()
        {
            if (serial == null)
                return;
            try
            {
                serial.Close();
            }
            catch (Exception ex)
            {
                ErrorLog(ex.ToString());
            }
        }

        public void Update()
        {
            if (serial == null) return;
            try
            {
                if (!serial.IsOpen || serial.BytesToRead <= 0)
                    return;
                byte[] buffer = new byte[serial.BytesToRead];
                int readLen = serial.Read(buffer, 0, buffer.Length);

                byte[] realBuffer = new byte[readLen];
                Buffer.BlockCopy(buffer, 0, realBuffer, 0, readLen);
                if (OnReceiveEvent != null)
                    OnReceiveEvent(realBuffer);
            }
            catch (Exception ex)
            {
                ErrorLog(ex.ToString());
                Close();
                return;
            }
        }

        private void ErrorLog(string log)
        {
            if (OnErrorLogEvent != null)
                OnErrorLogEvent(log);
        }

        /// <summary>
        /// 二进制数组转换为byte
        /// </summary>
        /// <param name="bits">例如:{1,0,0,0,0,0,0,0}表示128</param>
        /// <returns></returns>
        public static byte AssembleArray2Byte(byte[] bits)
        {
            if (bits.Length != 8)
                throw new Exception("bits 数据长度必须为8");

            byte ret = 0;
            for (int i = 0; i < 8; i++)
            {
                bits[i] = (byte)(bits[i] == 0 ? 0 : 1);
                ret |= (byte)(bits[i] << (7 - i));
            }
            return ret;
        }
        /// <summary>
        /// byte转换为二进制数组
        /// </summary>
        /// <param name="value"></param>
        /// <returns>二进制数组 例如:{1,0,0,0,0,0,0,0}表示128</returns>
        public static byte[] AssembleByte2Array(byte value)
        {
            byte[] ret = new byte[8];
            for (int i = 0; i < 8; i++)
            {
                byte[] arr = new byte[8];
                arr[7 - i] = 1;
                byte temp = value;
                temp &= AssembleArray2Byte(arr);
                ret[7 - i] = (byte)(temp >> i);
            }
            return ret;
        }

        /// <summary>
        /// 计算CRC16校验值
        /// </summary>
        /// <param name="data"></param>
        /// <param name="index"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static byte[] CalcCRC16(byte[] data, int index, int length)
        {
            int crc16 = 0xffff;
            for (int i = 0; i < length; i++)
            {
                crc16 = data[i + index] ^ crc16;
                for (int j = 0; j < 8; j++)
                {
                    if ((crc16 & 0x01) != 0)
                        crc16 = (crc16 >> 1) ^ 0xa001;
                    else
                        crc16 = crc16 >> 1;
                }
            }
            return BitConverter.GetBytes((ushort)crc16);
        }

        /// <summary>
        /// 计算length - 2 个长度的CRC16校验值，并把CRC16校验值赋值到data最后两个字节
        /// </summary>
        /// <param name="data"></param>
        /// <param name="index"></param>
        /// <param name="length"></param>
        public static void CombineCRC16(byte[] data, int index, int length)
        {
            byte[] crc16 = CalcCRC16(data, index, length - 2);
            data[index + length - 2] = crc16[0];
            data[index + length - 1] = crc16[1];
        }

        ~SerialPortEx()
        {
            Close();
        }
    }
}
